#include "fevent.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>

socket_ptr fevent_socket_malloc()
{
    /* Initialize event, These field need assign of caller */
    socket_ptr socket = (socket_ptr)calloc(1, sizeof(socket_t)); 
    return socket;
}

void fevent_socket_free(socket_ptr socket)
{
    free(socket); 
}

int fevent_create_socket(int port)
{
    /* Create a socket fd. */
    int socket_fd = socket(AF_INET, SOCK_STREAM, 0/*IPPROTO_TCP * Specity TCP link */);
    if (socket_fd == -1) {
        printf("[fevent_create_socket] socket error. port: %d, errno: %d\n", port, errno);
        return socket_fd; 
    }

    /* Set need bind related port */
    struct sockaddr_in socket_addr;
    socket_addr.sin_family = AF_INET;
    socket_addr.sin_port = htons(port);
    socket_addr.sin_addr.s_addr = htonl(INADDR_ANY); 

    /* Bind & Listen */
    if (bind(socket_fd, (struct sockaddr*)&socket_addr, sizeof(socket_addr)) == -1) {
        printf("[fevent_create_socket] bind error. port: %d, errno: %d\n", port, errno);
        return -1; 
    }
    if (listen(socket_fd, SOMAXCONN/*, QUEUE_SIZE*/) == -1) {
        printf("[fevent_create_socket] listen error. port: %d, errno: %d\n", port, errno);
        return -1; 
    }

    // printf("[fevent_create_socket] socket_fd: %d\n", socket_fd);

    return socket_fd;
}

/* Accept callback, Return 0 is success, -1 is failed. */
static int do_accept_callback(socket_ptr sock, socket_ptr * client_sock)
{
    int client_fd = -1;

    /* Get Client addr */
    struct sockaddr_in client_addr;
    unsigned int socket_len = sizeof(client_addr);
    if ((client_fd = accept(sock->fd, (struct sockaddr*)&client_addr, &socket_len)) == -1) {
        printf("[do_accept_callback] accept error. errno: %d\n", errno);
        return -1; 
    }

    socket_ptr client_sock_t = *client_sock;
    client_sock_t->fd = client_fd;
    // client_sock_t->port = client_addr.sin_port;

    return 0;
}

/* Read callback, Return received len. */
static int do_read_callback(socket_ptr client_sock, char** buffer, size_t len)
{
    memset(*buffer, 0, len);

    int recv_len = 0;

    while (1) {
        recv_len = recv(client_sock->fd, *buffer, len, 0/* */);  
        if (recv_len == -1) {
            if (errno != EAGAIN) {
                printf("[do_read_callback] read error. errno: %d\n", errno);
                break;
            } else {
                continue; 
            }
        }

        if (recv_len <= 0) {
            // printf("[do_read_callback] read error. errno: %d\n", errno);
            break;
        }

        break;
    }

    return recv_len;
}
/* Callback */
static int do_fevent_accept_callback(socket_ptr ptr, void* arg)
{
    // printf("[do_fevent_accept_callback]...\n");

    /* Create Client Socket */ 
    socket_ptr client_socket = fevent_socket_malloc();
    do_accept_callback(ptr, &client_socket);

    ((fevent_ptr)arg)->accept_func(client_socket, arg);

    /* Add Client Socket to Epoll Loop */
    return add_event((fevent_ptr)arg, client_socket);
}
static int do_fevent_read_callback(socket_ptr ptr, void* arg)
{
    // printf("[do_fevent_read_callback]...\n");

    char* buffer = (char*)malloc(1024);
    memset(buffer, 0, 1024);
    int recv_len = do_read_callback(ptr, &buffer, 1024);

    if (recv_len > 0) {
        // printf("[do_fevent_read_callback] recv_len: %d, buffer: %s\n", recv_len, buffer);
        ((fevent_ptr)arg)->read_func(ptr, buffer);
    } else {
        // printf("[do_fevent_read_callback] Client will close.\n");
        close(ptr->fd);
        if (del_event((fevent_ptr)arg, ptr) == -1) {
            printf("[do_fevent_read_callback] Remove event of fd: %d error.\n", ptr->fd);
        }
    }

    free(buffer);

    return 0;
}
/*static int do_fevent_write_callback(socket_ptr ptr, void* arg)
{
    printf("[do_fevent_write_callback]...\n");

    return 0;
}*/

/* Add event to Epoll */
int add_event(fevent_ptr fevent, socket_ptr socket)
{
    struct epoll_event event;
    event.events = EPOLLIN | EPOLLHUP;
    // event.data.fd = socket->fd;
    event.data.ptr = (void*)socket;

    return epoll_ctl(fevent->epoll_fd, EPOLL_CTL_ADD, socket->fd, &event);
}

int del_event(fevent_ptr fevent, socket_ptr socket)
{
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = socket->fd;
    event.data.ptr = (void*)socket;

    return epoll_ctl(fevent->epoll_fd, EPOLL_CTL_DEL, socket->fd, &event);
}

/* Malloc and free fevent */
fevent_ptr fevent_malloc(fevent_config_ptr config)
{
    if (NULL != config) {
        // printf("config: {%d, %s, %lu, %lu}\n", config->port, config->reuse?"true":"false", config->read_queue_size, config->write_queue_size);    
    }

    fevent_ptr fevent = (fevent_ptr)calloc(1, sizeof(fevent_t));
    if (NULL == fevent) {
        printf("calloc failed!");
        return fevent;
    }

    /* Initialize event, Need caller initialize */
    // fevent->accept_func   = do_fevent_accept_callback;
    // fevent->read_func     = do_fevent_read_callback;
    // fevent->write_func    = do_fevent_write_callback;

    fevent->origin_socket = fevent_socket_malloc();
    fevent->origin_socket->data = fevent;
    fevent->origin_socket->fd = fevent_create_socket(config->port);

    /* Create Epoll FD */
    fevent->epoll_fd = epoll_create(1024);
    if (fevent->epoll_fd == -1) {
        printf("[fevent_malloc] epoll_create failed! errno: %d\n", errno);
        free(fevent);
        return NULL;
    }

    /* Reset origin epoll event */
    fevent->origin_event.events = EPOLLIN;
    // fevent->origin_event.data.fd = fevent->origin_socket->fd;
    fevent->origin_event.data.ptr = (void*)fevent->origin_socket;

    if (add_event(fevent, fevent->origin_socket) == -1) {
        printf("[fevent_malloc] add_event failed! errno: %d\n", errno);
        fevent_socket_free(fevent->origin_socket);
        free(fevent);
        return NULL;
    }

    return fevent;
}
void fevent_free(fevent_ptr fevent)
{
    fevent_socket_free(fevent->origin_socket);
    free(fevent);
}

/* loop of fevent */
void fevent_loop(fevent_ptr fevent)
{
    struct epoll_event events[1024];
    // for (int i = 0; i < 1024; ++i)
    //    memset(&(events[i]), 0, sizeof(struct epoll_event));

    while(1) {
        /* Epoll wait */
        int num_event = 0; 
        if ((num_event = epoll_wait(fevent->epoll_fd, events, 1024, /*500*/ -1)) == -1) {
            printf("[fevent_loop] failed. errno: %d\n", errno);
            continue;
        }

        // if (num_event > 0) {
            // printf("[fevent_loop] num_event: %d\n", num_event);
        // }

        /* Processing each connection */
        for (int i = 0; i < num_event; ++i) {
            socket_ptr client_socket = (socket_ptr)events[i].data.ptr;
            // printf("[fevent_loop] data.fd: %d, origin_socket->fd: %d\n", client_socket->fd, fevent->origin_socket->fd);
            // printf("[fevent_loop] event.events: %d\n", events[i].events);
            if (client_socket->fd == fevent->origin_socket->fd) {
                /* Have a new connection */    
                do_fevent_accept_callback(fevent->origin_socket, fevent); 
            } else if (events[i].events & EPOLLIN) {
                /* Have a Readable connection */
                do_fevent_read_callback(events[i].data.ptr, fevent); 
            } else if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)) {
                close(client_socket->fd);
                if (del_event(fevent, client_socket) == -1) {
                    // printf("[fevent_loop] Remove event of fd: %d error.\n", client_socket->fd);
                }
            }
        }
    }
}
